package org.elec.mac.inter;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.elec.mac.constants.OrderConstants;
import org.elec.mac.ehcache.CacheConfiguration;
import org.elec.mac.entity.*;
import org.elec.mac.service.*;
import org.elec.mac.utils.AESUtil;
import org.elec.mac.utils.JSONUtil;
import org.elec.mac.utils.UnifiedOrder;
import org.elec.mac.utils.Utils;
import org.elec.mac.wxpay.WXPayConfigImpl;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;


@Slf4j
@Controller
@RequestMapping(value = "/pay")
public class NotifyController {

    @Value("${tencent.wx.appid}")
    private String appid;

    @Value("${tencent.wx.key}")
    private String key;

    @Value("${tencent.wx.mchid}")
    private String mchid;

    @Value("${cert.path}")
    private String certpath;

    @Value("${tencent.wx.notifyurl}")
    private String notify_url;

    @Value("${tencent.wx.refundurl}")
    private String refund_url;

    @Autowired
    private SNotifyOrderService notifyOrderService;

    @Autowired
    private SOrderService orderService;

    @Autowired
    private SOrderDetailService sOrderDetailService;

    @Autowired
    private SUnifiedOrderService sUnifiedOrderService;

    @Autowired
    private SOrderOuttradenoService outtradenoService;

    @Autowired
    private CacheConfiguration cacheConfiguration;

    @Autowired
    private SRefundService sRefundService;

    @Autowired
    private SProductService sProductService;

    @Autowired
    private SSaleService sSaleService;

    @Autowired
    private SSaleTotalService sSaleTotalService;

    @Autowired
    private SClientWxExtendService sClientWxExtendService;

    @RequestMapping(value = "/notify.do")
    private void notifyHandler(HttpServletRequest request, HttpServletResponse response) throws IOException {

        log.info("微信支付回调数据开始");

        //同步返回给微信结果
        String wx_return_code = "SUCCESS";
        // 微信支付回调
        String inputLine = null;
        // 接收到的数据
        StringBuffer recieveData = new StringBuffer();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(
                    request.getInputStream(), "UTF-8"));
            while((inputLine = in.readLine()) != null) {
                recieveData.append(inputLine);
            }


            log.info("支付成功的回调：" + recieveData);//返回的xml数据
            Map<String, String> resultMap = WXPayUtil.xmlToMap(recieveData.toString());//微信jdk 中一个XML转MAP的工具
            log.info("转换成Map数据结构" + resultMap);//将会显示{return_code=SUCCESS}

            String return_code = (String) resultMap.get("return_code");

            if("SUCCESS".equalsIgnoreCase(return_code)) {
                //通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
                response.getWriter().write("<xml><return_code><![CDATA[" + wx_return_code + "]]></return_code></xml>");
            } else {
                wx_return_code = "FAIL";
                response.getWriter().write("<xml><return_code><![CDATA[" + wx_return_code + "]]></return_code></xml>");
                return;
            }

            String oid = (String) resultMap.get("attach");

            Map<String, Object> param = new HashMap<>();
            param.put("id",oid);
            SOrder sOrder = orderService.get(param);
            if(sOrder==null){
                wx_return_code = "FAIL";
                response.getWriter().write("<xml><return_code><![CDATA[" + wx_return_code + "]]></return_code></xml>");
                return;
            }else{
                if(sOrder.getStatus().equals(OrderConstants.FINISH)){
                    return;
                }
            }

            String transaction_id = (String) resultMap.get("transaction_id");
            String nonce_str = (String) resultMap.get("nonce_str");
            String bank_type = (String) resultMap.get("bank_type");
            String openid = (String) resultMap.get("openid");
            String sign = (String) resultMap.get("sign");
            String fee_type = (String) resultMap.get("fee_type");
            String mch_id = (String) resultMap.get("mch_id");
            Integer cash_fee = Integer.parseInt(resultMap.get("cash_fee"));
            String out_trade_no = (String) resultMap.get("out_trade_no");
            String appid = (String) resultMap.get("appid");
            Integer total_fee = Integer.parseInt(resultMap.get("total_fee"));
            String trade_type = (String) resultMap.get("trade_type");
            String result_code = (String) resultMap.get("result_code");
            String time_end = (String) resultMap.get("time_end");
            String is_subscribe = (String) resultMap.get("is_subscribe");
            String machid = (String) resultMap.get("device_info");


            //签名验证
            if(WXPayUtil.isSignatureValid(resultMap, key, WXPayConstants.SignType.MD5)) {

                SNotifyOrder notify = new SNotifyOrder();
                notify.setAppid(appid);
                notify.setMchId(mch_id);
                notify.setDeviceInfo(machid);
                notify.setNonceStr(nonce_str);
                notify.setSign(sign);
                notify.setResultCode(result_code);
                notify.setOpenid(openid);
                notify.setIsSubscribe(is_subscribe);
                notify.setTradeType(trade_type);
                notify.setBankType(bank_type);
                notify.setTotalFee(total_fee);
                notify.setFeeType(fee_type);
                notify.setCashFee(cash_fee);
                notify.setTransactionId(transaction_id);
                notify.setOutTradeNo(out_trade_no);
                notify.setTimeEnd(time_end);

                if(notifyOrderService.update(notify) == 0) {
                    notifyOrderService.insert(notify);
                }

                SOrder order = new SOrder();
                order.setId(oid);
                order.setPayOrder(transaction_id);
                order.setReceiveno(oid.substring(14));

                SOrderOuttradeno outtradeno = new SOrderOuttradeno();
                outtradeno.setOid(oid);
                outtradeno.setOuttradeno(out_trade_no);

                if("SUCCESS".equalsIgnoreCase(result_code)) {
                    order.setRefund(false);//订单完成，不能退款
                    //记录订单流水

                } else if(result_code.equals("FAIL")) {
                    Map<String, String> body = new HashMap<>();
                    body.put("appid", appid);
                    body.put("mch_id", mch_id);
                    body.put("transaction_id", transaction_id);
                    body.put("nonce_str", nonce_str);
                    String sign1 = WXPayUtil.generateSignature(body, key);
                    String status = queryNotifyOrder(appid, mch_id, transaction_id, nonce_str, sign1);
                    if(status.equals("SUCCESS")) {
                        order.setRefund(true);//可以退款
                        try {
                            WXPayConfigImpl config = WXPayConfigImpl.getInstance(appid, mchid, key, certpath);
                            WXPay wxPay = new WXPay(config);
                            Map<String, String> para = new HashMap<>();
                            para.put("appid", appid);
                            para.put("mch_id", mchid);
                            para.put("nonce_str", WXPayUtil.generateNonceStr());
                            para.put("transaction_id", transaction_id);
                            para.put("out_refund_no", out_trade_no);
                            para.put("total_fee", order.getAcprice().toString());
                            para.put("refund_fee", order.getAcprice().toString());
                            para.put("notify_url", refund_url);
                            para.put("sign", WXPayUtil.generateSignature(para, key));
                            log.info("退款参数：" + para);
                            wxPay.refund(para);
                        } catch(Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        order.setRefund(false);//不能退款
                    }
                } else {
                    throw new Exception();
                }

                outtradeno.setStatus(OrderConstants.FINISH);
                order.setStatus(OrderConstants.FINISH);
                order.setModifyTime(new Date());
                outtradenoService.update(outtradeno);
                orderService.update(order);

                if(machid != null) {
                    long mid = Long.parseLong(machid);
                    param.clear();
                    param.put("oid", oid);
                    List<SOrderDetail> detailList = sOrderDetailService.list(param);
                    for(SOrderDetail sOrderDetail : detailList) {
                        param.clear();
                        param.put("id", sOrderDetail.getPid());
                        SProduct sProduct = sProductService.get(param);

                        SSale sSale = new SSale();
                        sSale.setOid(oid);
                        sSale.setMachid(mid);
                        sSale.setCateid(sProduct.getCid());
                        sSale.setPid(sProduct.getId());
                        sSale.setSale(new Long(sOrderDetail.getCounts()));
                        sSale.setPprice(sProduct.getPprice() * sSale.getSale());
                        sSale.setOprice(sOrderDetail.getPrice() * sSale.getSale());
                        sSale.setSprice(sOrderDetail.getCprice() * sSale.getSale());
                        sSale.setProfit(sSale.getSprice() - sSale.getPprice());
                        sSale.setCreateTime(new Date());
                        sSaleService.insertSaleProduct(sSale);

                        param.clear();
                        param.put("machid", mid);
                        param.put("cateid", sProduct.getCid());
                        param.put("pid", sProduct.getId());
                        param.put("createTime", new Date());
                        SSaleTotal sSaleTotal = sSaleTotalService.get(param);

                        if(sSaleTotal == null) {
                            sSaleTotal = new SSaleTotal();
                            sSaleTotal.setPid(sProduct.getId());
                            sSaleTotal.setMachid(mid);
                            sSaleTotal.setCateid(sProduct.getCid());
                            sSaleTotal.setSale(new Long(sOrderDetail.getCounts()));
                            sSaleTotal.setAmount(sOrderDetail.getCprice() * sSaleTotal.getSale());
                            sSaleTotal.setProfit(sSale.getSprice() - sSale.getPprice());
                            sSaleTotal.setCreateDate(new Date());
                            sSaleTotalService.insertSaleProductTotal(sSaleTotal);
                        } else {
                            sSaleTotal.setPid(sProduct.getId());
                            sSaleTotal.setMachid(mid);
                            sSaleTotal.setCateid(sProduct.getCid());
                            sSaleTotal.setSale(sSaleTotal.getSale() + new Long(sOrderDetail.getCounts()));
                            sSaleTotal.setAmount(sSaleTotal.getAmount() + sOrderDetail.getCprice() * sSaleTotal.getSale());
                            sSaleTotal.setProfit(sSaleTotal.getProfit() + sSale.getSprice() - sSale.getPprice());
                            sSaleTotalService.updateVipOrder(sSaleTotal);
                        }
                    }

                    EhCacheCacheManager cacheCacheManager = cacheConfiguration.ehCacheCacheManager(cacheConfiguration.ehCacheManagerFactoryBean());
                    Cache wsCache = cacheCacheManager.getCache("webSocketCache");
                    WebSocketSession session = wsCache.get(machid, WebSocketSession.class);
                    Map<String, Object> result = new HashMap<>();
                    result.put("orderid", oid);
                    result.put("status", result_code);
                    result.put("openid", openid);
                    Map<String, Object> para = new HashMap<>();
                    para.put("id", oid);
                    SOrder sorder = orderService.get(para);
                    if(Objects.equals(sorder.getStatus(), OrderConstants.FINISH)) {
                        TextMessage textMessage = new TextMessage(JSONUtil.map2json(result));
                        session.sendMessage(textMessage);
                    }
                }



                param.clear();
                param.put("openid", openid);
                SClientWxExtend sClientWxExtend = sClientWxExtendService.get(param);
                param.put("id", oid);
                order = orderService.get(param);
                if(sClientWxExtend != null) {
                    sClientWxExtend.setOrdercount(sClientWxExtend.getOrdercount() + 1);
                    sClientWxExtend.setTotal(sClientWxExtend.getTotal() + order.getTotal());
                    sClientWxExtend.setIntegral(sClientWxExtend.getIntegral() + 10);
                    sClientWxExtend.setSprice(sClientWxExtend.getSprice() + (order.getTprice() - order.getAcprice()));
                    sClientWxExtend.setCprice(sClientWxExtend.getCprice() + order.getAcprice());
                    sClientWxExtendService.update(sClientWxExtend);
                }
            }

        } catch(Exception e) {
            // TODO Auto-generated catch block
            wx_return_code = "FAIL";
            log.error("微信回调接口出现错误：", e);
            response.getWriter().write("<xml><return_code><![CDATA[" + wx_return_code + "]]></return_code></xml>");
            e.printStackTrace();
        }
    }


    @RequestMapping(value = "/refund.do")
    private void refundHadler(HttpServletRequest request, HttpServletResponse response) throws IOException {

        log.info("微信退款回调数据开始");

        //同步返回给微信结果
        String wx_return_code = "SUCCESS";
        // 微信支付回调
        String inputLine = null;
        // 接收到的数据
        StringBuffer recieveData = new StringBuffer();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(
                    request.getInputStream(), "UTF-8"));
            while((inputLine = in.readLine()) != null) {
                recieveData.append(inputLine);
            }


            log.info("退款成功的回调：" + recieveData);//返回的xml数据
            Map<String, String> resultMap = WXPayUtil.xmlToMap(recieveData.toString());//微信jdk 中一个XML转MAP的工具
            log.info("转换成Map数据结构" + resultMap);//将会显示{return_code=SUCCESS}

            //通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
            response.getWriter().write("<xml><return_code><![CDATA[" + wx_return_code + "]]></return_code></xml>");

            String A = resultMap.get("req_info");
            log.info("加密字段：" + A);
            String B = AESUtil.decryptData(A);
            log.info("解密字段：\n" + B);

            Map<String, String> decrypt = WXPayUtil.xmlToMap(B);
            String out_trade_no = decrypt.get("out_trade_no");
            String out_refund_no = decrypt.get("out_refund_no");
            String refund_account = decrypt.get("refund_account");
            String refund_fee = decrypt.get("refund_fee");
            String refund_id = decrypt.get("refund_id");
            String refund_recv_accout = decrypt.get("refund_recv_accout");
            String refund_request_source = decrypt.get("refund_request_source");
            String refund_status = decrypt.get("refund_status");
            String settlement_refund_fee = decrypt.get("settlement_refund_fee");
            String settlement_total_fee = decrypt.get("settlement_total_fee");
            String success_time = decrypt.get("success_time");
            String total_fee = decrypt.get("total_fee");
            String transaction_id = decrypt.get("transaction_id");

            Map<String, Object> param = new HashMap<>();
            param.put("outTradeno", out_trade_no);
            SOrder order = orderService.get(param);
            order.setRefstat(1);
            orderService.update(order);


            SRefund sRefund = new SRefund();
            sRefund.setOutRefundNo(out_refund_no);
            sRefund.setOutTradeNo(out_trade_no);
            sRefund.setRefundAccount(refund_account);
            sRefund.setRefundFee(Utils.parseInt(refund_fee, 0));
            sRefund.setRefundId(refund_id);
            sRefund.setRefundRecvAccout(refund_recv_accout);
            sRefund.setRefundRequestSource(refund_request_source);
            sRefund.setRefundStatus(refund_status);
            sRefund.setSettlementRefundFee(Utils.parseInt(settlement_refund_fee, 0));
            sRefund.setSettlementTotalFee(Utils.parseInt(settlement_total_fee, 0));
            sRefund.setTotalFee(Utils.parseInt(total_fee, 0));
            sRefund.setTransactionId(transaction_id);
            sRefund.setSuccessTime(success_time);
            sRefundService.insert(sRefund);
            log.info("退款成功！");

        } catch(Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 查询微信那边的订单
     *
     * @param appid
     * @param mch_id
     * @param transaction_id
     * @param nonce_str
     * @param sign
     * @return
     * @throws Exception
     */
    private String queryNotifyOrder(String appid, String mch_id, String transaction_id, String nonce_str, String sign) throws Exception {
        //查询订单
        Map<String, String> reqData = new HashMap<>();
        reqData.put("appid", appid);
        reqData.put("mch_id", mch_id);
        reqData.put("transaction_id", transaction_id);
        reqData.put("nonce_str", nonce_str);
        reqData.put("sign", sign);

        WXPayConfigImpl config = WXPayConfigImpl.getInstance(appid, mchid, key, certpath);
        WXPay wxPay = new WXPay(config);
        Map<String, String> responseParam = wxPay.orderQuery(reqData);
        System.out.println(responseParam);
        String trade_state = responseParam.get("trade_state");

        return trade_state;
    }

    @RequestMapping(value = "/scannotify.do")
    private void scavengingHandler(HttpServletRequest request, HttpServletResponse response) throws IOException {
        log.info("扫码支付回调数据开始");

        String inputLine = null;
        // 接收到的数据
        StringBuffer recieveData = new StringBuffer();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(
                    request.getInputStream(), "UTF-8"));
            while((inputLine = in.readLine()) != null) {
                recieveData.append(inputLine);
            }

            log.info("扫码成功的回调：" + recieveData);//返回的xml数据
            Map<String, String> resultMap = WXPayUtil.xmlToMap(recieveData.toString());//微信jdk 中一个XML转MAP的工具
            log.info("转换成Map数据结构" + resultMap);//将会显示{return_code=SUCCESS}

            String[] orderid_mach = resultMap.get("product_id").split("_");
            String orderid = orderid_mach[0];
            Map<String, Object> param = new HashMap<>();
            param.put("id", orderid);
            SOrder order = orderService.get(param);

            String resXml = "";//反馈给微信服务器
            // 验签
            if(WXPayUtil.isSignatureValid(resultMap, key)) {
                long total_fee = order.getAcprice();//标价金额 注意：价格的单位是分
                String spbill_create_ip = Utils.getIpAddress(request);
                String trade_type = "NATIVE";//交易类型
                String machserial = orderid_mach[1];
                String openid = (String) resultMap.get("openid");

                param.clear();
                param.put("openid", openid);
                SClientWxExtend sClientWxExtend = sClientWxExtendService.get(param);
                if(sClientWxExtend == null) {
                    sClientWxExtend = new SClientWxExtend();
                    sClientWxExtend.setOpenid(openid);
                    sClientWxExtend.setCprice((long)0);
                    sClientWxExtend.setSprice((long)0);
                    sClientWxExtend.setTotal(0);
                    sClientWxExtend.setIntegral(0);
                    sClientWxExtend.setOrdercount(0);
                    sClientWxExtend.setCreatetime(new Date());
                    sClientWxExtendService.insert(sClientWxExtend);
                }

                SOrder o = new SOrder();
                o.setId(orderid);
                o.setUserInfo(openid);
                orderService.update(o);

                JSONObject resParams = new JSONObject();
                int code = 0;
                resParams.put("code", code);
                resParams.put("msg", "成功");

                JSONObject result = UnifiedOrder.order(orderid, machserial, openid, trade_type, resParams, orderid, spbill_create_ip, total_fee, appid, mchid, key, certpath, notify_url, sUnifiedOrderService).getJSONObject("body");

                Map<String, String> res = new HashMap<>();
                String prepayid = ((String) result.get("package")).split("=")[1];
                res.put("return_code", "SUCCESS"); // 必须
                res.put("return_msg", "OK");
                res.put("appid", appid); // 必须
                res.put("mch_id", mchid);//必须
                res.put("nonce_str", WXPayUtil.generateNonceStr()); // 必须
                res.put("prepay_id", prepayid); // 必须
                res.put("result_code", "SUCCESS"); // 必须
                res.put("err_code_des", "OK");
                res.put("sign", WXPayUtil.generateSignature(res, key)); //签名

                resXml = WXPayUtil.mapToXml(res);
                log.info(resXml);

            } else {
                log.info("签名验证错误");
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
            }

            //------------------------------
            //处理业务完毕
            //------------------------------
            response.getWriter().write(resXml);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
